// Copyright Epic Games, Inc. All Rights Reserved.

#include "ToonLightMyRendering.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "PrimitiveSceneProxy.h"
#include "MeshPassProcessor.inl"
#include "ScenePrivate.h"
#include "DeferredShadingRenderer.h"
#include "RenderCore.h"


//实现ToonOutlinePassRender.h内定义的方法
FRDGTextureDesc GetTBufferShadowTextureDesc(FIntPoint Extent, ETextureCreateFlags CreateFlags)
{
	//Extent:虚幻引擎的贴图尺寸，PF_B8G8R8A8：贴图的色彩格式，RGBA均为8bit
	//FClearValueBinding::clear贴图资源时将其重置为黑色
	//TexCreate_UAV：Unordered Access View 允许在shader中进行随机读写
	//TexCreate_RenderTargetable：表示纹理可以作为渲染目标使用
	//TexCreate_ShaderResource：表示纹理可以作为shader资源，可以在shader中被采样等操作
	return FRDGTextureDesc(FRDGTextureDesc::Create2D(Extent, PF_B8G8R8A8, FClearValueBinding::Black, TexCreate_UAV | TexCreate_RenderTargetable | TexCreate_ShaderResource | CreateFlags));
};
//创建贴图纹理
FRDGTextureRef CreateTBufferShadowTexture(FRDGBuilder& GraphBuilder, FIntPoint Extent, ETextureCreateFlags CreateFlags)
{
	return GraphBuilder.CreateTexture(GetTBufferShadowTextureDesc(Extent, CreateFlags), TEXT("ShadowTexture"));
};

class FLightMyPassVS : public FMeshMaterialShader
{
public:
	DECLARE_SHADER_TYPE(FLightMyPassVS, MeshMaterial);

	static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters,
		FShaderCompilerEnvironment& OutEnvironment){}

	static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
	{

		return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
	}

	FLightMyPassVS(){};
	FLightMyPassVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		: FMeshMaterialShader(Initializer)
	{}

    void GetShaderBindings(
	const FScene* Scene,
	ERHIFeatureLevel::Type FeatureLevel,
	const FPrimitiveSceneProxy* PrimitiveSceneProxy,
	const FMaterialRenderProxy& MaterialRenderProxy,
	const FMaterial& Material,
	const FMeshMaterialShaderElementData& ShaderElementData,
	FMeshDrawSingleShaderBindings& ShaderBindings) const
	{
		FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, ShaderElementData, ShaderBindings);
	}
};

class FLightMyPassPS : public FMeshMaterialShader
{
public:
	DECLARE_SHADER_TYPE(FLightMyPassPS, MeshMaterial);

	static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
	{
		return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
	}

	static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
	{
		FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
		OutEnvironment.SetDefine(TEXT("ToonLightMy"), 1);
	}

	FLightMyPassPS(){};
	FLightMyPassPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		: FMeshMaterialShader(Initializer)
	{}

    void GetShaderBindings(
	const FScene* Scene,
	ERHIFeatureLevel::Type FeatureLevel,
	const FPrimitiveSceneProxy* PrimitiveSceneProxy,
	const FMaterialRenderProxy& MaterialRenderProxy,
	const FMaterial& Material,
	const FMeshMaterialShaderElementData& ShaderElementData,
	FMeshDrawSingleShaderBindings& ShaderBindings) const
	{
		FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, ShaderElementData, ShaderBindings);
	}
};

IMPLEMENT_SHADER_TYPE(, FLightMyPassVS, TEXT("/Engine/Private/Toon/ToonLightMyPassShader.usf"), TEXT("MainVS"), SF_Vertex);
IMPLEMENT_SHADER_TYPE(, FLightMyPassPS, TEXT("/Engine/Private/Toon/ToonLightMyPassShader.usf"), TEXT("MainPS"), SF_Pixel);
IMPLEMENT_SHADERPIPELINE_TYPE_VSPS(LightMyPipeline, FLightMyPassVS, FLightMyPassPS, true);


bool GetLightMyPassShaders(
	const FMaterial& Material,
	const FVertexFactoryType* VertexFactoryType,
	ERHIFeatureLevel::Type FeatureLevel,
	TShaderRef<FLightMyPassVS>& VertexShader,
	TShaderRef<FLightMyPassPS>& PixelShader,
	FShaderPipelineRef& ShaderPipeline)
{
	FMaterialShaderTypes ShaderTypes;
	// ShaderTypes.PipelineType = &LightMyPassPipeline;
	ShaderTypes.AddShaderType<FLightMyPassVS>();
	ShaderTypes.AddShaderType<FLightMyPassPS>();

	FMaterialShaders Shaders;
	if (!Material.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders))
	{
		return false;
	}

	Shaders.TryGetPipeline(ShaderPipeline);
	Shaders.TryGetVertexShader(VertexShader);
	Shaders.TryGetPixelShader(PixelShader);

	return true;
}

void FLightMyPassMeshProcessor::AddMeshBatch(
	const FMeshBatch& RESTRICT MeshBatch, 
	uint64 BatchElementMask, 
	const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, 
	int32 StaticMeshId /* = -1 */ 
	)
{
	if (MeshBatch.bUseForMaterial)
	{
		const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
		while (MaterialRenderProxy)
		{
			const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
			if (Material && Material->GetRenderingThreadShaderMap())
			{
				const FMaterialShadingModelField ShadingModels = Material->GetShadingModels();
				// 只有材质使用了Toon相关的shading model才会被绘制
				if (ShadingModels.HasShadingModel(MSM_ToonDef) || ShadingModels.HasShadingModel(MSM_ToonFace) || ShadingModels.HasShadingModel(MSM_ToonMore))
				{
					// const EBlendMode BlendMode = Material->GetBlendMode();
					// if (BlendMode == BLEND_Opaque)
					// {
						if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
						{
							break;
						}
					// }
				}
			}

			MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
		}
	}
}

bool FLightMyPassMeshProcessor::TryAddMeshBatch(
	const FMeshBatch& RESTRICT MeshBatch,
	uint64 BatchElementMask,
	const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
	int32 StaticMeshId,
	const FMaterialRenderProxy& MaterialRenderProxy,
	const FMaterial& Material)
{
	bool bResult = false;
	if ((!PrimitiveSceneProxy || PrimitiveSceneProxy->ShouldRenderInMainPass())
		&& ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain()))
	{
		const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
		const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
		const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);

		bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, MeshFillMode, MeshCullMode);
	}

	return bResult;
}

bool FLightMyPassMeshProcessor::Process(
	const FMeshBatch& MeshBatch, 
	uint64 BatchElementMask, 
	int32 StaticMeshId, 
	const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
	const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, 
	const FMaterial& RESTRICT MaterialResource,
	ERasterizerFillMode MeshFillMode, 
	ERasterizerCullMode MeshCullMode 
	)
{
	const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
	const FIndexBuffer* IndexBuffer = MeshBatch.Elements[0].IndexBuffer;

	TMeshProcessorShaders<FLightMyPassVS,FLightMyPassPS> LightMyPassShaders;

	FShaderPipelineRef ShaderPipeline;

	if (!GetLightMyPassShaders(
		MaterialResource,
		VertexFactory->GetType(),
		FeatureLevel,
		LightMyPassShaders.VertexShader,
		LightMyPassShaders.PixelShader,
		ShaderPipeline))
	{
		return false;
	}

	FMeshMaterialShaderElementData ShaderElementData;
	ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);

	const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(LightMyPassShaders.VertexShader, LightMyPassShaders.PixelShader);

	FMeshPassProcessorRenderState DrawRenderState(PassDrawRenderState);

	BuildMeshDrawCommands(
		MeshBatch,
		BatchElementMask,
		PrimitiveSceneProxy,
		MaterialRenderProxy,
		MaterialResource,
		PassDrawRenderState,
		LightMyPassShaders,
		MeshFillMode,
		MeshCullMode,
		SortKey,
		EMeshPassFeatures::Default,
		ShaderElementData
		);

	return true;
}


IMPLEMENT_STATIC_UNIFORM_BUFFER_SLOT(LightMyPassData);
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FLightMyPassBuffer, "LightMyPassData", LightMyPassData);

// // class FMyComputeShader : public FGlobalShader
// // {
// // public:
// // 	//注册全局着色器参数
// // 	DECLARE_GLOBAL_SHADER(FMyComputeShader);
// // 	SHADER_USE_PARAMETER_STRUCT(FMyComputeShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FLightMyPassParameters, )
	// SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
	SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
	SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
	SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLightMyPassBuffer,LightMyPassData)
	SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
	RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
// // };

// // IMPLEMENT_GLOBAL_SHADER(FMyComputeShader, "/Engine/Private/Toon/ToonLightMyPassPassShader.usf", "MainPS", SF_Pixel);

TRDGUniformBufferRef<FLightMyPassBuffer> CreateLightMyPassPassUniformBuffer(
	FRDGBuilder& GraphBuilder,
	const FViewInfo& View,
	const FScene* Scene,
	FSceneTextures& SceneTextures)
{
	FLightMyPassBuffer& LightMyPassParameters = *GraphBuilder.AllocParameters<FLightMyPassBuffer>();
	LightMyPassParameters.LightParameters.Color = FVector3f::Zero();
	LightMyPassParameters.LightParameters.Direction = FVector3f::Zero();
	LightMyPassParameters.LightParameters.SourceRadius = 0;
	LightMyPassParameters.LightParameters.SoftSourceRadius = 0;

	const FLightSceneInfo* MainLight = Scene->AtmosphereLights[0];
	if(MainLight)
	{
		FLightRenderParameters LightParameters;
		MainLight->Proxy->GetLightShaderParameters(LightParameters,0);
		LightParameters.MakeShaderParameters(View.ViewMatrices,View.GetLastEyeAdaptationExposure(),LightMyPassParameters.LightParameters);
	}

	LightMyPassParameters.ToonShadowTexture = SceneTextures.ToonShadow;
    LightMyPassParameters.ToonShadowTextureSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();

	LightMyPassParameters.SceneTextureCopy = SceneTextures.Color.Target;
    LightMyPassParameters.SceneTextureCopySampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();

	return GraphBuilder.CreateUniformBuffer(&LightMyPassParameters);
}

DECLARE_GPU_DRAWCALL_STAT_NAMED(LightMyPass, TEXT("Light My Pass"));
// DECLARE_CYCLE_STAT(TEXT("LightMyPass"), STAT_CLP_LightMyPass, STATGROUP_ParallelCommandListMarkers);

// BEGIN_SHADER_PARAMETER_STRUCT(FLightMyPassPassParameeters, )
// 	SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
// 	SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
// 	RENDER_TARGET_BINDING_SLOTS()
// END_SHADER_PARAMETER_STRUCT()

void FDeferredShadingSceneRenderer::RenderLightMyPass(
	FRDGBuilder& GraphBuilder,
	TArrayView<FViewInfo> InViews,
	FSceneTextures& SceneTextures
)
{
	// RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderLightMyPass);
	// SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderLightMyPass, FColor::Emerald);
	//SCOPE_CYCLE_COUNTER(STAT_LightMyPassPassDrawTime);
	RDG_GPU_STAT_SCOPE(GraphBuilder, LightMyPass);
	RDG_EVENT_SCOPE(GraphBuilder, "LightMy Pass");

	for (int32 ViewIndex = 0; ViewIndex < InViews.Num(); ++ViewIndex)
	{
		FViewInfo& View = InViews[ViewIndex];
		RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
		RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, InViews.Num() > 1, "View%d", ViewIndex);


		const bool bShouldRenderView = View.ShouldRenderView();
		if (bShouldRenderView)
		{
			View.BeginRenderView();

			// FMeshPassProcessorRenderState DrawRenderState;
			// SetupBasePassState(DrawRenderState);

			auto* PassParameters = GraphBuilder.AllocParameters<FLightMyPassParameters>();
			// PassParameters->View = View.GetShaderParameters();
			PassParameters->View = View.ViewUniformBuffer;
			PassParameters->LightMyPassData = CreateLightMyPassPassUniformBuffer(GraphBuilder, View, Scene, SceneTextures);
			// PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad,
			// 	FExclusiveDepthStencil::DepthRead_StencilWrite); //DepthWrite_StencilNop
			PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite);
			PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.Color.Target, ERenderTargetLoadAction::ELoad);
			// PassParameters->RenderTargets[1] = FRenderTargetBinding(SceneTextures.MyTextTexture, ERenderTargetLoadAction::EClear);

			View.ParallelMeshDrawCommandPasses[EMeshPass::LightMyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);

			GraphBuilder.AddPass(
				RDG_EVENT_NAME("LightMyPass"),
				PassParameters,
				ERDGPassFlags::Raster,
				[this, &View, PassParameters](FRHICommandList& RHICmdList)
				{
					SetStereoViewport(RHICmdList, View, 1.0f);
					View.ParallelMeshDrawCommandPasses[EMeshPass::LightMyPass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
				});
			// 	ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
			// 	[this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
			// {
			// 	FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_LightMyPass), View, FParallelCommandListBindings(PassParameters));
			// 	ParallelCommandListSet.SetHighPriority();
			// 	View.ParallelMeshDrawCommandPasses[EMeshPass::LightMyPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
			// });
		}
	}
}

void SetupBasePassState(FMeshPassProcessorRenderState& DrawRenderState)
{
	DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
	
}

FLightMyPassMeshProcessor::FLightMyPassMeshProcessor(
	EMeshPass::Type InMeshPassType,
	const FScene* Scene, 
	ERHIFeatureLevel::Type InFeatureLevel,
	const FSceneView* InViewIfDynamicMeshCommand,
	const FMeshPassProcessorRenderState& InPassDrawRenderState, 
	FMeshPassDrawListContext* InDrawListContext
	)
	: FMeshPassProcessor(InMeshPassType, Scene, InFeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext)
	, PassDrawRenderState(InPassDrawRenderState)
{
	if (PassDrawRenderState.GetDepthStencilState() == nullptr)
	{
		PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>().GetRHI());
	}
	if (PassDrawRenderState.GetBlendState() == nullptr)
	{
		PassDrawRenderState.SetBlendState(TStaticBlendState<>().GetRHI());
	}
}

FMeshPassProcessor* CreateLightMyPassProcessor(ERHIFeatureLevel::Type InFeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
{
	const ERHIFeatureLevel::Type FeatureLevel = InViewIfDynamicMeshCommand ? InViewIfDynamicMeshCommand->GetFeatureLevel() : InFeatureLevel;

	FMeshPassProcessorRenderState PassDrawRenderState;
	SetupBasePassState(PassDrawRenderState);

	return new FLightMyPassMeshProcessor(EMeshPass::LightMyPass, Scene, InFeatureLevel, InViewIfDynamicMeshCommand, PassDrawRenderState, InDrawListContext);
}

REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(LightMyPass, CreateLightMyPassProcessor, EShadingPath::Deferred, EMeshPass::LightMyPass, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);